#include "engine_impl.hh"
#include "../corelib/logger/writer.hh"
#include "../corelib/logger/appender.hh"
#include "../corelib/logger/file_appender.hh"
#include "../corelib/util/os_util.hh"
#include "../corelib/util/string_util.hh"

namespace kratos { namespace engine {

bool EngineImpl::parseConfig() {
    if (!config_ || !config_->has("engine.grid")) {
        return true;
    }
    if (config_->has("engine.grid.identity")) {
        std::size_t domain = 0;
        std::size_t type = 0;
        std::size_t host = 0;
        if (config_->has("engine.grid.identity.domain")) {
            domain = config_->getUint32("engine.grid.identity.domain");
        } else {
            throw std::runtime_error("engine.grid.domain not found");
        }
        if (config_->has("engine.grid.identity.type")) {
            type = config_->getUint32("engine.grid.identity.type");
        } else {
            throw std::runtime_error("engine.grid.type not found");
        }
        if (config_->has("engine.grid.identity.host")) {
            host = config_->getUint32("engine.grid.identity.host");
        } else {
            throw std::runtime_error("engine.grid.host not found");
        }
        if (domain > UINT16_MAX) {
            throw std::runtime_error("engine.grid.domain must less than " + std::to_string(UINT16_MAX));
        }
        if (type > UINT16_MAX) {
            throw std::runtime_error("engine.grid.type must less than " + std::to_string(UINT16_MAX));
        }
        if (host > UINT32_MAX) {
            throw std::runtime_error("engine.grid.host must less than " + std::to_string(UINT32_MAX));
        }
        grid_->property().domain = static_cast<std::uint16_t>(domain);
        grid_->property().type = static_cast<std::uint16_t>(type);
        grid_->property().host = static_cast<std::uint32_t>(host);
        grid_->property().id.reset(static_cast<std::uint32_t>(host), static_cast<std::uint16_t>(domain),
            static_cast<std::uint16_t>(type));
        if (config_->has("engine.grid.identity.isLeader")) {
            auto s = config_->getString("engine.grid.identity.isLeader");
            grid_->property().isLeader = (s == "true") ? true : false;
        }
        if (config_->has("engine.grid.identity.isRouter")) {
            auto s = config_->getString("engine.grid.identity.isRouter");
            grid_->property().isRouter = (s == "true") ? true : false;
        }
    } else {
        throw std::runtime_error("engine.grid not found");
    }
    if (config_->has("engine.grid.localAddress")) {
        auto localAddress = config_->get("engine.grid.localAddress");
        if (localAddress->isString()) {
            grid_->property().localAddress.push_back(config_->getString("engine.grid.localAddress"));
        } else if (localAddress->isArray()) {
            auto addressArray = localAddress->array();
            for (auto i = 0; i < addressArray->getSize(); i++) {
                grid_->property().localAddress.push_back(addressArray->string(i)->get());
            }
        } else {
            throw std::runtime_error("engine.grid.localAddress must be a 'string' or 'string array'");
        }
    }
    else {
        throw std::runtime_error("engine.grid.localAddress not found");
    }
    if (config_->has("engine.grid.leaderAddress")) {
        auto leaderAddress = config_->get("engine.grid.leaderAddress");
        if (leaderAddress->isString()) {
            grid_->property().localAddress.push_back(config_->getString("engine.grid.leaderAddress"));
        } else if (leaderAddress->isArray()) {
            auto addressArray = leaderAddress->array();
            for (auto i = 0; i < addressArray->getSize(); i++) {
                grid_->property().leaderAddress.push_back(addressArray->string(i)->get());
            }
        } else {
            throw std::runtime_error("engine.grid.leaderAddress must be a 'string' or 'string array'");
        }
    }
    if (config_->has("engine.grid.joinTimeout")) {
        grid_->property().joinTimeout = config_->getUint32("engine.grid.joinTimeout");
    }
    if (config_->has("engine.grid.pingTimeout")) {
        grid_->property().pingTimeout = config_->getUint32("engine.grid.pingTimeout");
    }
    if (config_->has("engine.grid.joinBroadcast")) {
        auto s = config_->getString("engine.grid.joinBroadcast");
        grid_->property().joinBroadcast = (s == "true") ? true : false;
    }
    if (config_->has("engine.grid.loopService")) {
        auto s = config_->getString("engine.grid.loopService");
        grid_->property().loopService = (s == "true") ? true : false;
    }
    if (config_->has("engine.grid.siteDupPolicy")) {
        auto s = config_->getString("engine.grid.siteDupPolicy");
        grid_->property().siteDupPolicy = grid::getDupPolicy(s);
    }
    if (config_->has("engine.grid.path")) {
        if (config_->has("engine.grid.path.serviceFindTimeout")) {
            findTimeout_ = config_->getUint32("engine.grid.path.serviceFindTimeout");
        }
        if (config_->has("engine.grid.path.maxHop")) {
            grid_->property().maxHop = config_->getUint8("engine.grid.path.maxHop");
        }
        if (config_->has("engine.grid.path.closePing")) {
            auto s = config_->getString("engine.grid.path.closePing");
            grid_->property().closePing = (s == "true") ? true : false;
        }
        if (config_->has("engine.grid.path.findType")) {
            auto s = config_->getString("engine.grid.path.findType");
            grid_->property().findType = grid::getFindType(s);
        }
        if (config_->has("engine.grid.path.prefind")) {
            auto prefind = config_->array("engine.grid.path.prefind");
            for (auto i = 0; i < prefind->getSize(); i++) {
                prefindMap_[static_cast<SiteType>(prefind->number(i)->getInt())] = false;
            }
        }
    }
    if (config_->has("engine.grid.whiteList")) {
        auto list = config_->array("engine.grid.whiteList");
        for (auto i = 0; i < list->getSize(); i++) {
            grid_->property().siteWhiteList.push_back(static_cast<std::uint16_t>(list->number(i)->getUint()));
        }
    }
    if (config_ && config_->has("engine.gateway")) {
        auto localAddress = config_->array("engine.gateway.localAddress");
        for (auto i = 0; i < localAddress->getSize(); i++) {
            grid_->property().gatewayLocalAddress.push_back(localAddress->string(i)->get());
        }
        if (config_->has("engine.gateway.sessionTimeout")) {
            grid_->property().gatewaySessionTimeout = config_->getUint32("engine.gateway.sessionTimeout");
        }
    }
    if (config_ && config_->has("engine.module.plugin")) {
        auto plugin = config_->string("engine.module.plugin")->get();
        moduleManager_.addToWhite(plugin);
    }
    if (config_ && config_->has("engine.grid.netBuffSize")) {
        auto buffSize = config_->getUint32("engine.grid.netBuffSize");
        grid_->property().bufSize = buffSize;
    }
    if (config_ && config_->has("engine.locator.type")) {
        auto hashType = config_->string("engine.locator.type")->get();
        if (hashType == "circle") {
            hashType_ = HashType::HASH_TYPE_CIRCLE;
        } else if (hashType == "hashMode") {
            hashType_ = HashType::HASH_TYPE_MOD;
        } else {
            throw std::runtime_error("engine.locator.type is unknown type");
        }
    }
    return true;
}

bool EngineImpl::startLogger() {
    if (!config_ || !config_->has("engine.logger")) {
        return true;
    }
    if (config_->has("engine.logger.level")) {
        auto level = config_->getString("engine.logger.level");
        loggerRef.setLevel(getLevel(level));
    }
    if (config_->has("engine.logger.prefix")) {
        auto prefix = config_->getString("engine.logger.prefix");
        if (!loggerRef.formatter().parse(prefix)) {
            ENGINE_LOG_ERROR("Logger line prefix error:", prefix);
            return false;
        }
    }
    if (config_->has("engine.logger.type")) {
        auto type = config_->getString("engine.logger.type");
        auto appenderType = getAppender(type);
        if (appenderType == AppenderType::File) {
            auto& fileAppender = dynamic_cast<corelib::FileAppender&>(loggerRef.selectAppender(appenderType));
            if (config_->has("engine.logger.splitMode")) {
                auto splitMode = config_->getString("engine.logger.splitMode");
                fileAppender.setSplitMode(corelib::getSplitMode(splitMode));
            }
            if (config_->has("engine.logger.path")) {
                auto path = config_->getString("engine.logger.path");
                if (!corelib::OS::isExists(path)) {
                    loggerRef.selectAppender(AppenderType::Console);
                    ENGINE_LOG_ERROR("Logger file path not exists:", path);
                    return false;
                }
                fileAppender.setPath(path);
            }
            if (config_->has("engine.logger.fileName")) {
                auto fileName = config_->getString("engine.logger.fileName");
                if (!fileAppender.start(fileName)) {
                    loggerRef.selectAppender(AppenderType::Console);
                    ENGINE_LOG_ERROR("Logger start failed with name:", fileName);
                    return false;
                }
            } else {
                if (!fileAppender.start("%y%m%d%H%M%S")) {
                    loggerRef.selectAppender(AppenderType::Console);
                    ENGINE_LOG_ERROR("Logger start failed with name:", "%y%m%d%H%M%S");
                    return false;
                }
            }
        } else {
            loggerRef.selectAppender(appenderType).start("");
        }
    } else {
        // Console logger as default
        loggerRef.selectAppender(AppenderType::Console).start("");
    }
    return true;
}

}}
